package control;

import java.util.Set;
import model.CivilPlace;
import model.Civilization;
import model.Empire;
import model.Player;
import model.Province;
import vinciappli.CardId;


public class ControllerPrincipal {

    private static final ControllerPrincipal INSTANCE = new ControllerPrincipal();
    private ControllerGameBoard controllerGameBoard = new ControllerGameBoard();
    private ControllerPlayer controllerPlayer = new ControllerPlayer();

    /**
     * Must be called to start the controller part of the app as it sets up the gameboard
     */
    public void launch()
    {
        controllerGameBoard.ini();
        controllerGameBoard.startGame();
    }

    /**
     * Fill parameters of the following game
     * @param initialPts : Starting points for all players
     * @param maxPts : Needed points to win
     */
    public void gameSetUp(Integer initialPts, Integer maxPts)
    {
        if(initialPts != null)
        {
            controllerGameBoard.getGameBoard().setInitialPts(initialPts);
        }
        if(maxPts != null)
        {
            controllerGameBoard.getGameBoard().setMaxPts(maxPts);
        }
    }

    /**
     * Add a player to the following game
     * @param name : Players's ID
     * @param isComputer : true if the added player is computer, false if he's not
     * @return : true if player's well added, false if max number of players is reached
     */
    public boolean addPlayer(String name, String urlAvatar, boolean isComputer)
    {
        if(controllerPlayer.getListPlayers().size() == 6)
        {
            return false;
        }
        else
        {
            controllerPlayer.getListPlayers().add(new Player(name, urlAvatar, isComputer));
            return true;
        }
    }

    /**
     * Start a game
     * <br>- Set up the gameboard with elements of the game (cards, pawns,...)
     * <br>- Set players'params
     * <br>- Get the starting player
     */
    public void startGame()
    {
        
        controllerPlayer.ini();

        controllerPlayer.setPlayersTurn(controllerPlayer.getRandomPlayer());
        controllerGameBoard.setNumCurrentPlayer(0);
    }

    /**
     * Set a new empire with the civilization a player has chosen
     * <br>- Civilization start calculation of points
     * <br>- Set the selected civilization into the new player's empire
     * <br>- Card "espions", "specialisation" supported
     * @param position : Position of the civilization on the gameboard (between 1 and 6)
     */
    public Civilization selectCivilization(int position)
    {
        CivilPlace selected = controllerGameBoard.getGameBoard().getCivilizations().get(position);
        int pts = ((position) * -2) + selected.getBonusPts();
        int troops = selected.getCivilization().getInitialPts();
        if(controllerGameBoard.getNbRound() == 0)
        {
            troops +=  controllerGameBoard.getSpecialPts();
        }
        controllerGameBoard.getCurrentPlayer().setPointsFromCurrentValue(pts);
        controllerGameBoard.getCurrentPlayer().setTroopsFromCurrentNumber(troops);
        controllerGameBoard.getCurrentPlayer().setAvailableTroopsFromCurrentNumber(troops);

        controllerGameBoard.getCurrentPlayer().getEmpires().add(
                                                new Empire(controllerGameBoard.getGameBoard().getCivilizations().get(position).getCivilization()));
        
        controllerGameBoard.civilizationPickUp(position);
        if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.espions))
        {
            if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
            {
                controllerGameBoard.getCurrentPlayer().setSpyTimes(2);
            }
            else
            {
                controllerGameBoard.getCurrentPlayer().setSpyTimes(1);
            }
        }

        return selected.getCivilization();
    }

    /**
     * Select a province as the first one of an empire for the current player
     * @param destination : Selected province to conquer
     * @return ...
     */
    public boolean selectFirstProvince(Province destination)
    {
        
        int cost = controllerGameBoard.getConquestCost(null, destination, controllerGameBoard.getCurrentPlayer());
        if(goConquest(cost, destination))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Make the conquest from a province of current player empire to another province (owned by another player or not)
     * @param from : Province from the assailant player
     * @param destination : Province which is going to be conquered
     * @param spy : true if the player wants to use the card "espions" advantage
     * @return true if the player can make the conquest, false if he can't (he doesn't have as much available troops as it's required
     */
    public boolean selectProvince(Province from, Province destination, boolean spy)
    {
        if(controllerGameBoard.getPotentialProvinces(controllerGameBoard.getCurrentPlayer()).contains(destination))
        {
            int cost = 0;

            if(spy && controllerGameBoard.getCurrentPlayer().canSpy())
            {
                cost = 2;
                controllerGameBoard.getCurrentPlayer().setSpyTimes(-1);
            }
            else
            {
                cost = controllerGameBoard.getConquestCost(from, destination, controllerGameBoard.getCurrentPlayer());
            }

            if(goConquest(cost, destination))
            {
                return true;
            }
            else
            {
                // TODO
                System.out.println("Le joueur ne dispose pas d'assez de troupes pour conquérir la province choisie");
                return false;
            }
        }
        else
        {
            // TODO
            System.out.println("La province choisie n'est pas accessible pour l'empire du joueur");
            return false;
        }
        
    }

    /**
     * Set an empire as depressed
     * <br>- If an older empire is depressed, it's removed and its territory occupation is updated
     * <br>- The active empire is set as depressed (cards removed, territory occupation updated, status updated)
     * @param e : The on-going depressed empire
     */
    public void goDepress(Player p)
    {
        Empire e = p.getEmpire(false);
        if(p.getEmpire(true) != null)
        {
            controllerGameBoard.removeEmpire(p);
        }
        controllerGameBoard.goDepressEmpire(e);
    }

    /**
     * Valid the conquest of a selected province
     * @param cost : The total amount of troops needed to conquer the selected province
     * @param destination : The province which is going to be conquered
     * @return true if the conquest could be done, false otherwise
     */
    public boolean goConquest(int cost, Province destination)
    {
        if(controllerGameBoard.getCurrentPlayer().getAvailableTroops() >= cost)
        {
            destination.beConquered(controllerGameBoard.getCurrentPlayer(), cost);
            controllerGameBoard.getCurrentPlayer().setAvailableTroopsFromCurrentNumber(-cost);
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Start the defense stage of a player's turn
     * <br>- The player can move his troops through his empire to be prepared from attacks
     * <br>- Cards "general", "specialisation", "forts" supported
     */
    public int goDefense()
    {
        Player p = controllerGameBoard.getCurrentPlayer();
        int bonusTroops = 0;
        if(p.getEmpire(false).getCivilization().isComposedOf(CardId.general))
        {
            bonusTroops = -7;
        }
        if(p.getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
        {
            bonusTroops = bonusTroops * 2;
        }

        if(p.getAvailableTroops() < (-bonusTroops))
        {
            return (-bonusTroops) - p.getAvailableTroops();
        }
        else
        {
            p.setAvailableTroopsFromCurrentNumber(bonusTroops);
            p.setTroopsFromCurrentNumber(bonusTroops);
        }
        // TODO S'assurer que le joueur a 7 troupes non posées avant de lui retirer et le laisser organiser ses défenses

        if(p.getEmpire(false).getCivilization().isComposedOf(CardId.fortsDec))
        {
            if(p.getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
            {
                p.setAvailableForts(2);
            }
            else
            {
                p.setAvailableForts(1);
            }
        }
        return 0;
        // TODO : Phase où le joueur place ses troupes en formation de défense, et éventuellement place un fort
    }

    /**
     * Pass turn to another player and check if the game is going to end
     */
    public boolean nextPlayer()
    {
        controllerGameBoard.nextRound();
        if(controllerGameBoard.getNumCurrentPlayer() == 0 && controllerPlayer.getBestPlayer().getPoints() >= controllerGameBoard.getGameBoard().getMaxPts())
        {
            return true;
        }
        return false;
    }

    /**
     * Called to set up the gameboard considering the new player's turn
     * <br>- Cards "general", "medecine", "diplomatie", "specialisation" supported
     * @param friend : Player selected who can't attack or be attacked for this turn (could be null)
     */
    public int openingTurn(Player friend)
    {
        controllerGameBoard.getNewProvinces().clear();
        int bonusTroops = 0;
        try
        {
            if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.general))
            {
                bonusTroops = 7;
            }
            if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.medecine))
            {
                bonusTroops = 1;
            }
            if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
            {
                bonusTroops = bonusTroops * 2;
            }
            if(controllerGameBoard.getCurrentPlayer().getEmpire(false).getCivilization().isComposedOf(CardId.diplomatie) && friend != null)
            {
                controllerGameBoard.getCurrentPlayer().setFriend(friend);
            }
            controllerGameBoard.getCurrentPlayer().setAvailableTroopsFromCurrentNumber(bonusTroops);
            controllerGameBoard.getCurrentPlayer().setTroopsFromCurrentNumber(bonusTroops);
        }
        catch(NullPointerException e)
        {
            // Le joueur actif n'a pas encore d'empire
        }
        // TODO
        return bonusTroops;
    }

    /**
     * Closing action of a player's turn
     * <br>- Check if the player's active empire is unified
     * <br>- Check if others have to order available troops attacked in the current turn
     * <br>- Calculation of current player's points
     * <br>- Cards "espions", "messages", "diplomatie", "specialisation" supported
     * @param p
     */
    public void closingTurn()
    {
        Player p = controllerGameBoard.getCurrentPlayer();

        if(!p.getEmpires().isEmpty() && controllerGameBoard.getNbRound() != 0)
        {
            if(p.getEmpire(false) != null)
            {
                Set<Province> listNeededProvincesToGetUnifiedEmpire = p.getEmpire(false).isUnified();
                if(!p.getEmpire(false).getCivilization().isComposedOf(CardId.messages) && listNeededProvincesToGetUnifiedEmpire != null)
                {
                    // TODO Action si l'empire n'est pas d'un seul bloc
                }
                
                if(p.getEmpire(false).getCivilization().isComposedOf(CardId.espions))
                {
                    if(p.getEmpire(false).getCivilization().isComposedOf(CardId.specialisation))
                    {
                        p.setSpyTimes(2);
                    }
                    else
                    {
                        p.setSpyTimes(1);
                    }
                }

                if(p.getEmpire(false).getCivilization().isComposedOf(CardId.diplomatie))
                {
                    p.setFriend(null);
                }
            }

            if(!controllerGameBoard.getIdleTroops().isEmpty())
            {
                // TODO Donner la main aux joueurs ayant des troupes à replacer dans leur empire
                controllerGameBoard.getIdleTroops().clear();
            }
            int val = controllerGameBoard.ptsCalculation(p);
            p.setPointsFromCurrentValue(val);
        }
    }

    /**
     * Called when a player has just won the game
     * @param winner : Winning player
     */
    public void closingGame(Player winner)
    {
        
    }

    public void resetGame()
    {
        controllerGameBoard = new ControllerGameBoard();
        controllerPlayer = new ControllerPlayer();
    }

    /***************************** GETTERS & SETTERS **************************/

    public static ControllerPrincipal getINSTANCE() {
        return INSTANCE;
    }

    public ControllerGameBoard getControllerGameBoard() {
        return controllerGameBoard;
    }

    public ControllerPlayer getControllerPlayer() {
        return controllerPlayer;
    }

    
}

